/**
 * ReflectUtil.java
 * cn.ccig.core.common.util
 * Copyright (c) 2014, 北京聚智未来科技有限公司版权所有.
*/

package com.we.es.util;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.LinkedList;
import java.util.List;

import org.nutz.log.Log;
import org.nutz.log.Logs;

import com.uxuexi.core.common.util.CollectionUtil;
import com.uxuexi.core.common.util.ExceptionUtil;
import com.uxuexi.core.common.util.Util;

/**
 * 反射工具类
 *
 * @author   朱晓川
 * @Date	 Aug 4, 2014 	 
 */
public class ReflectionUtil {
	/**
	* Logger for this class
	*/
	private static final Log log = Logs.get();

	/**
	 * 获取声明的属性集合，循环追溯父类至根，并将每一个属性强制设置为可访问
	 * 
	 * <p>
	 * 如果不设置指定获取的属性类型，则返回全部
	 *
	 * @param <T> 属性类型
	 * @param obj 待计算对象
	 * @param fieldType 指定的属性类型
	 * @return 声明的属性集合；如果待计算对象为Null，则返回空集合
	 */
	public static <T> Field[] getFields(final Object obj, final Class<T> fieldType) {
		if (Util.isEmpty(obj)) {
			return CollectionUtil.array();
		}
		List<Field> fieldList = new LinkedList<Field>();
		for (Class<?> clazz = obj.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) {
			for (Field field : clazz.getDeclaredFields()) {
				if (Util.isEmpty(fieldType)) {
					field.setAccessible(true);
					fieldList.add(field);
					continue;
				}
				if (!Util.eq(fieldType, field.getType())) {
					continue;
				}
				field.setAccessible(true);
				fieldList.add(field);
			}
		}
		if (Util.isEmpty(fieldList)) {
			return CollectionUtil.array();
		}

		return CollectionUtil.collection2array(fieldList, Field.class);
	}

	/**
	 * 获取对象声明的全部属性，循环追溯父类至根，并将每一个属性强制设置为可访问
	 */
	public static <T> Field[] getFields(final Object obj) {
		return getFields(obj, null);
	}

	/**
	 * 获取对象obj的声明了某个注解的所有属性字段,循环追溯父类至根,并将每一个属性字段强制设置为可访问。
	 * <P>
	 * 返回数组类型
	 * 
	 * TODO 重构
	 * @param obj              对象
	 * @param AnnotationType   注解类型
	 */
	public static <T> Field[] fieldArrayByAnnotation(final Object obj, final Class<? extends Annotation> AnnotationType) {
		return CollectionUtil.collection2array(fieldListByAnnotation(obj, AnnotationType), Field.class);
	}

	/**
	 * 获取对象obj的声明了某个注解的所有属性字段,循环追溯父类至根,并将每一个属性字段强制设置为可访问。
	 * <P>
	 * 返回集合类型
	 * 
	 * TODO 重构
	 * @param obj              对象
	 * @param AnnotationType   注解类型
	 */
	public static List<Field> fieldListByAnnotation(final Object obj, final Class<? extends Annotation> AnnotationType) {
		if (Util.isEmpty(obj)) {
			return CollectionUtil.list();
		}
		List<Field> fieldList = new LinkedList<Field>();
		for (Class<?> clazz = obj.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) {
			for (Field field : clazz.getDeclaredFields()) {
				if (!field.isAnnotationPresent(AnnotationType)) {
					continue;
				}
				field.setAccessible(true);
				fieldList.add(field);
			}
		}
		if (Util.isEmpty(fieldList)) {
			return CollectionUtil.list();
		}

		return fieldList;
	}

	/**
	 * 获取声明的内部类，循环追溯父类至根。
	 * <p>
	 * 内部类包含枚举类
	 *
	 * @param obj 待计算对象
	 * @return 声明的内部类集合；如果待计算对象为Null，则返回空集合
	 */
	public static Class<?>[] getClasses(final Object obj) {
		if (Util.isEmpty(obj)) {
			return CollectionUtil.array();
		}
		List<Class<?>> classes = new LinkedList<Class<?>>();
		for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
			for (Class<?> oneClass : superClass.getDeclaredClasses()) {
				classes.add(oneClass);
			}
		}
		if (Util.isEmpty(classes)) {
			return CollectionUtil.array();
		}

		return CollectionUtil.collection2array(classes, Class.class);
	}

	/**
	 * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数.
	 */
	public static Object getFieldValue(final Object obj, final String fieldName) {
		Field field = getAccessibleField(obj, fieldName);
		return getFieldValue(obj, field);
	}

	/**
	 * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数.
	 */
	public static Object getFieldValue(final Object obj, final Field field) {
		if (field == null) {
			throw ExceptionUtil.pEx("Reflect.Failed", obj, field);
		}

		Object result = null;
		try {
			result = field.get(obj);
		} catch (IllegalAccessException e) {
			log.error("不可能抛出的异常:" + e.getMessage());
		}
		return result;
	}

	/**
	 * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数.
	 */
	public static void setFieldValue(final Object obj, final String fieldName, final Object value) {
		Field field = getAccessibleField(obj, fieldName);

		if (field == null) {
			throw ExceptionUtil.pEx("Reflect.Failed", obj, fieldName);
		}
		try {
			field.set(obj, value);
		} catch (IllegalAccessException e) {
			log.error("不可能抛出的异常:" + e.getMessage());
		}
	}

	/**
	 * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问.
	 * 
	 * 如向上转型到Object仍无法找到, 返回null.
	 */
	public static Field getAccessibleField(final Object obj, final String fieldName) {
		if (Util.isEmpty(obj)) {
			throw ExceptionUtil.pEx("Null", "obj");
		}
		if (Util.isEmpty(fieldName)) {
			throw ExceptionUtil.pEx("Null", "fieldName");
		}
		for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
			try {
				Field field = superClass.getDeclaredField(fieldName);
				field.setAccessible(true);
				return field;
			} catch (NoSuchFieldException e) {// NOSONAR
				// Field不在当前类定义,继续向上转型
			}
		}
		return null;
	}

	/**
	 * 直接调用对象方法, 无视private/protected修饰符. 用于一次性调用的情况.
	 */
	public static Object invokeMethod(final Object obj, final String methodName, final Class<?>[] parameterTypes,
			final Object[] args) {
		if (Util.isEmpty(obj)) {
			throw ExceptionUtil.pEx("Null", "obj");
		}
		Method method = getAccessibleMethod(obj, methodName, parameterTypes);
		if (method == null) {
			throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + obj + "]");
		}

		try {
			return method.invoke(obj, args);
		} catch (Exception e) {
			throw ExceptionUtil.pEx("invokeMethod", e);
		}
	}

	/**
	 * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. 如向上转型到Object仍无法找到, 返回null.
	 * 
	 * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object...
	 * args)
	 */
	public static Method getAccessibleMethod(final Object obj, final String methodName,
			final Class<?>... parameterTypes) {
		if (Util.isEmpty(obj)) {
			throw ExceptionUtil.pEx("Null", "obj");
		}

		for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
			try {
				Method method = superClass.getDeclaredMethod(methodName, parameterTypes);

				method.setAccessible(true);

				return method;

			} catch (NoSuchMethodException e) {// NOSONAR
				// Method不在当前类定义,继续向上转型
			}
		}
		return null;
	}

	/**
	 * 通过反射, 获得Class定义中声明的父类的泛型参数的类型. 如无法找到, 返回Object.class. eg. public UserDao
	 * extends HibernateDao<User>
	 * 
	 * @param clazz
	 *            The class to introspect
	 * @return the first generic declaration, or Object.class if cannot be
	 *         determined
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static <T> Class<T> getSuperClassGenricType(final Class clazz) {
		return getSuperClassGenricType(clazz, 0);
	}

	/**
	 * 通过反射, 获得Class定义中声明的父类的泛型参数的类型. 如无法找到, 返回Object.class.
	 * 
	 * 如public UserDao extends HibernateDao<User,Long>
	 * 
	 * @param clazz clazz The class to introspect
	 *            
	 * @param index  the Index of the generic ddeclaration,start from 0.
	 *           
	 * @return the index generic declaration, or Object.class if cannot be
	 *         determined
	 */
	@SuppressWarnings("rawtypes")
	public static Class getSuperClassGenricType(final Class clazz, final int index) {
		if (Util.isEmpty(clazz)) {
			throw ExceptionUtil.pEx("Null", "clazz");
		}
		Type genType = clazz.getGenericSuperclass();

		if (!(genType instanceof ParameterizedType)) {
			log.info(clazz.getSimpleName() + "'s superclass not ParameterizedType");
			return Object.class;
		}

		Type[] params = ((ParameterizedType) genType).getActualTypeArguments();

		if (index >= params.length || index < 0) {
			log.warn("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: "
					+ params.length);
			return Object.class;
		}
		if (!(params[index] instanceof Class)) {
			log.warn(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");
			return Object.class;
		}

		return (Class) params[index];
	}
}
